package com.anxinr.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.anxinr.constant.RedisConstant;
import com.anxinr.enums.ShoppingInfoEnum;
import com.anxinr.exception.LogicException;
import com.anxinr.mapper.GoodsMapper;
import com.anxinr.mapper.ShoppingInfoMapper;
import com.anxinr.pojo.dto.ShoppingDto;
import com.anxinr.pojo.entity.Goods;
import com.anxinr.pojo.entity.ShoppingInfo;
import com.anxinr.pojo.entity.ShoppingOrder;
import com.anxinr.service.GoodsService;
import com.anxinr.service.ShoppingInfoService;
import com.anxinr.service.ShoppingOrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.logging.LogException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.rmi.runtime.Log;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author : anxinr
 * @date : 2023/2/3 11:46
 */
@Service
public class ShoppingInfoServiceImpl extends ServiceImpl<ShoppingInfoMapper, ShoppingInfo> implements ShoppingInfoService {

    @Autowired
    private StringRedisTemplate redisTemplate ;

    @Autowired
    private GoodsService goodsService ;

    @Autowired
    private ShoppingOrderService orderService ;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ShoppingInfo buying(ShoppingDto info) {
        /**信息判空*/
        if (ObjectUtil.isNull(info.getGoodsId())) {
            throw new LogicException("商品id不能为空!") ;
        }

        if (ObjectUtil.isNull(info.getUserId())) {
            throw new LogicException("用户id不能为空!") ;
        }


        /**第一步 : 分布式锁*/
        Boolean lockResult = redisTemplate.opsForValue().setIfAbsent(
                RedisConstant.USER_SHOPPING_GOODS +
                        info.getUserId() +
                        RedisConstant.REDIS_MATE +
                        info.getGoodsId(),
                info.getUserId() +
                        RedisConstant.REDIS_MATE +
                        info.getGoodsId() ,
                RedisConstant.USER_SHOPPING_GOODS_TIME ,
                TimeUnit.MINUTES
        );
        /**是否需要释放锁*/
        boolean releaseLock = true ;
        System.out.println("lockResult = " + lockResult);
        if (lockResult) {
            try{


                /**第二步 : 查询缓存库存*/
                String goodsInventory = redisTemplate.opsForValue().get(
                        RedisConstant.GOODS_CACHE_INVENTORY +
                                info.getGoodsId()
                );
                if (StringUtils.isBlank(goodsInventory)) {
                    throw new LogicException("该商品已经下架!") ;
                }
                Integer goodsInventoryInt = Integer.valueOf(goodsInventory);
                /**没有库存  */
                // TODO: 2023/9/22 还有一个情况是，目前库存小于  0 先暂时不用管
                if (goodsInventoryInt <= 0){
                    throw new LogicException("该商品目前没有库存!") ;
                }else {
                    /**库存 减一 */
                    redisTemplate.opsForValue().increment(
                            RedisConstant.GOODS_CACHE_INVENTORY +
                            info.getGoodsId() ,
                            -1) ;
                }


                /**第三步 : 查询mysql 是否购买过 如果购买过，那么现在三个小时内不能够再操作*/
                LambdaQueryWrapper<ShoppingInfo> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(ShoppingInfo::getUserId ,info.getUserId()) ;
                wrapper.eq(ShoppingInfo::getGoodsId ,info.getGoodsId()) ;
                wrapper.eq(ShoppingInfo::getState , ShoppingInfoEnum.SHOPPING_INFO_PAY.getCode()) ;
                List<ShoppingInfo> shoppingInfos = this.list(wrapper);
                /**表示已经购买过，返回，并且锁住当前用户和商品*/
                if (CollectionUtils.isNotEmpty(shoppingInfos)) {
                    /**时间续费到三个小时*/
                    redisTemplate.expire(
                            RedisConstant.USER_SHOPPING_GOODS +
                            info.getUserId() +
                            RedisConstant.REDIS_MATE +
                            info.getGoodsId() ,
                            RedisConstant.LOCK_USER_GOODS_TIME ,
                            TimeUnit.MINUTES
                            ) ;
                    releaseLock = false ;
                    throw new LogException("你已经购买过该商品了，请勿重复下单操作!") ;
                }


                /**第四步 : 生成购物信息*/
                ShoppingInfo shoppingInfo = new ShoppingInfo();
                shoppingInfo.setGoodsId(info.getGoodsId());
                shoppingInfo.setUserId(info.getUserId());
                shoppingInfo.setCreateTime(new Date());
                shoppingInfo.setState(ShoppingInfoEnum.SHOPPING_INFO_PAY.getCode());
                this.save(shoppingInfo) ;


                /**第五步 : 减库存*/
                boolean reduceInventory = goodsService.reduceInventory(info.getGoodsId());
                if (!reduceInventory) {
                    throw new LogicException("减库存失败!") ;
                }


                /**第六步 : 发送订单消息  */
                // TODO: 2023/9/22 目前使用直接调用，后期切换到  mq  异步发送
                ShoppingOrder order = new ShoppingOrder();
                order.setGoodsId(info.getGoodsId());
                order.setUserId(info.getUserId());
                order.setCreateTime(new Date());
                order.setState(ShoppingInfoEnum.SHOPPING_INFO_UN_PAY.getCode());
                return shoppingInfo;


            }catch (Exception e){
                /**异常处理，刷新库存*/
                Goods goods = goodsService.getById(info.getGoodsId());
                redisTemplate.opsForValue().set(
                        RedisConstant.GOODS_CACHE_INVENTORY +
                                info.getGoodsId() ,
                        goods.getInventory() + ""
                );
                throw e ;
            }finally {
                /**释放锁*/
                if (releaseLock) {
                    redisTemplate.delete(RedisConstant.USER_SHOPPING_GOODS +
                            info.getUserId() +
                            RedisConstant.REDIS_MATE +
                            info.getGoodsId()) ;
                }
            }
        }else {
            throw new LogicException("请勿重复操作!") ;
        }

    }
}
